#pragma once

#ifndef __BIPL_TYPES__
#define __BIPL_TYPES__
#include <string>
#include <iostream>
#include <assert.h>
#include <Magick++.h> 
#include <boost/filesystem.hpp>

//using namespace Magick; 
using namespace boost::filesystem;

/// Declarations
class boolTypes;
class intTypes;
class floatTypes;
class degreeTypes;
class pairTypes;
class stringTypes;
class imageTypes;

/// Define the abstract parent class
class generalTypes {
public:
	virtual boolTypes toBoolean() const = 0;
	virtual intTypes toInteger() const = 0;
	virtual floatTypes toFloat() const = 0;
	virtual stringTypes toString() const = 0;
	virtual pairTypes toPair() const = 0;
	virtual degreeTypes toDegree() const = 0;
	//virtual generalTypes toImage() {};
private:
};


///////////////////////////////////////
/// BOOL
///////////////////////////////////////
class boolTypes : public generalTypes {
public:
	/// Constructors
	boolTypes():val(false) {}
	boolTypes(bool _val):val(_val) {}

	/// Type conversion functions
	boolTypes toBoolean() const;
	intTypes toInteger() const;
	floatTypes toFloat() const;
	degreeTypes toDegree() const;
	pairTypes toPair() const;
	stringTypes toString() const;

	/// implicitly convert to bool
	operator bool() const;
	/// implicitly convert to other types
	operator intTypes() const;
	operator floatTypes() const;
	operator degreeTypes() const;
	operator pairTypes() const;
	operator stringTypes() const;

	/// output stream operator
	friend std::ostream& operator<<(std::ostream& out, const boolTypes& s);

	/// Negation Operator
	friend intTypes operator- (const boolTypes &s);
	/// Arithmetic Operators
	friend const intTypes operator+(const boolTypes& lhs, const boolTypes& rhs);
	friend const intTypes operator-(const boolTypes& lhs, const boolTypes& rhs);
	friend const intTypes operator/(const boolTypes& lhs, const boolTypes& rhs);
	friend const intTypes operator*(const boolTypes& lhs, const boolTypes& rhs);
	friend const intTypes operator%(const boolTypes& lhs, const boolTypes& rhs);
	/// Arithmetic Assignment Operators
	friend intTypes operator+=(boolTypes& lhs, const boolTypes& rhs);
	friend intTypes operator-=(boolTypes& lhs, const boolTypes& rhs);
	friend intTypes operator*=(boolTypes& lhs, const boolTypes& rhs);
	friend intTypes operator/=(boolTypes& lhs, const boolTypes& rhs);
	friend intTypes operator%=(boolTypes& lhs, const boolTypes& rhs);
	/// Equality Operators
	friend boolTypes operator==(const boolTypes& left, const boolTypes& right);
	friend boolTypes operator!=(const boolTypes& left, const boolTypes& right);
	/// Relational Operators
	friend boolTypes operator<(const boolTypes& left, const boolTypes& right);
	friend boolTypes operator>(const boolTypes& left, const boolTypes& right);
	friend boolTypes operator<=(const boolTypes& left, const boolTypes& right);
	friend boolTypes operator>=(const boolTypes& left, const boolTypes& right);
	/// Logical Operators
	friend boolTypes operator!(const boolTypes& s);
	friend boolTypes operator&&(const boolTypes& left, const boolTypes& right);
	friend boolTypes operator||(const boolTypes& left, const boolTypes& right);
protected:
	bool val;
};

///////////////////////////////////////
/// INTEGER
///////////////////////////////////////
class intTypes : public generalTypes {
public:
	/// Constructors
	intTypes():val(0) {}
	intTypes(int _val):val(_val) {}

	/// Type conversion functions
	boolTypes toBoolean() const;
	intTypes toInteger() const;
	floatTypes toFloat() const;
	degreeTypes toDegree() const;
	pairTypes toPair() const;
	stringTypes toString() const;

	/// implicitly convert to bool
	operator bool() const;
	/// implicitly convert to other types
	operator const floatTypes() const;
	operator const degreeTypes() const;
	operator const pairTypes() const;
	operator const stringTypes() const;

	/// output stream operator
	friend std::ostream& operator<<(std::ostream& out, const intTypes& s);

	/// Negation Operator
	friend intTypes operator- (const intTypes &s);
	/// Arithmetic Operators
	friend const intTypes operator+(const intTypes& lhs, const intTypes& rhs);
	friend const intTypes operator-(const intTypes& lhs, const intTypes& rhs);
	friend const intTypes operator/(const intTypes& lhs, const intTypes& rhs);
	friend const intTypes operator*(const intTypes& lhs, const intTypes& rhs);
	friend const intTypes operator%(const intTypes& lhs, const intTypes& rhs);
	/// Arithmetic Assignment Operators
	friend intTypes& operator+=(intTypes& lhs, const intTypes& rhs);
	friend intTypes& operator-=(intTypes& lhs, const intTypes& rhs);
	friend intTypes& operator*=(intTypes& lhs, const intTypes& rhs);
	friend intTypes& operator/=(intTypes& lhs, const intTypes& rhs);
	friend intTypes& operator%=(intTypes& lhs, const intTypes& rhs);
	/// Equality Operators
	friend boolTypes operator==(const intTypes& left, const intTypes& right);
	friend boolTypes operator!=(const intTypes& left, const intTypes& right);
	/// Relational Operators
	friend boolTypes operator<(const intTypes& left, const intTypes& right);
	friend boolTypes operator>(const intTypes& left, const intTypes& right);
	friend boolTypes operator<=(const intTypes& left, const intTypes& right);
	friend boolTypes operator>=(const intTypes& left, const intTypes& right);
	/// Logical Operators
	friend boolTypes operator!(const intTypes& s);
	friend boolTypes operator&&(const intTypes& left, const intTypes& right);
	friend boolTypes operator||(const intTypes& left, const intTypes& right);

	int getVal() const {return val;}
protected:
	int val;
};


///////////////////////////////////////
/// FLOAT
///////////////////////////////////////
class floatTypes : public generalTypes {
public:
	/// Constructors
	floatTypes():val(0.0f) {}
	floatTypes(float _val):val(_val) {}

	/// Type conversion functions
	boolTypes toBoolean() const;
	intTypes toInteger() const;
	floatTypes toFloat() const;
	degreeTypes toDegree() const;
	pairTypes toPair() const;
	stringTypes toString() const;

	/// implicitly convert to bool
	operator bool() const;
	/// implicitly convert to other types
	operator const degreeTypes() const;
	operator const pairTypes() const;
	operator const stringTypes() const;

	/// output stream operator
	friend std::ostream& operator<<(std::ostream& out, const floatTypes& s);

	/// Negation Operator
	friend floatTypes operator- (const floatTypes &s);
	/// Arithmetic Operators
	friend const floatTypes operator+(const floatTypes& lhs, const floatTypes& rhs);
	friend const floatTypes operator-(const floatTypes& lhs, const floatTypes& rhs);
	friend const floatTypes operator/(const floatTypes& lhs, const floatTypes& rhs);
	friend const floatTypes operator*(const floatTypes& lhs, const floatTypes& rhs);
	friend const floatTypes operator%(const floatTypes& lhs, const floatTypes& rhs);
	/// Arithmetic Assignment Operators
	friend floatTypes& operator+=(floatTypes& lhs, const floatTypes& rhs);
	friend floatTypes& operator-=(floatTypes& lhs, const floatTypes& rhs);
	friend floatTypes& operator*=(floatTypes& lhs, const floatTypes& rhs);
	friend floatTypes& operator/=(floatTypes& lhs, const floatTypes& rhs);
	friend floatTypes& operator%=(floatTypes& lhs, const floatTypes& rhs);
	/// Equality Operators
	friend boolTypes operator==(const floatTypes& left, const floatTypes& right);
	friend boolTypes operator!=(const floatTypes& left, const floatTypes& right);
	/// Relational Operators
	friend boolTypes operator<(const floatTypes& left, const floatTypes& right);
	friend boolTypes operator>(const floatTypes& left, const floatTypes& right);
	friend boolTypes operator<=(const floatTypes& left, const floatTypes& right);
	friend boolTypes operator>=(const floatTypes& left, const floatTypes& right);
	/// Logical Operators
	friend boolTypes operator!(const floatTypes& s);
	friend boolTypes operator&&(const floatTypes& left, const floatTypes& right);
	friend boolTypes operator||(const floatTypes& left, const floatTypes& right);

	float getVal() const {return val;}
protected:
	float val;
};


///////////////////////////////////////
/// DEGREE
///////////////////////////////////////
class degreeTypes : public generalTypes {
public:
	/// Constructors
	degreeTypes():val(0) {}
	degreeTypes(float _val):val(_val) {}
	explicit degreeTypes(const intTypes &_val) {val = float(_val.getVal());}
	explicit degreeTypes(const floatTypes &_val) {val = _val.getVal();}

	/// Type conversion functions
	boolTypes toBoolean() const;
	intTypes toInteger() const;
	floatTypes toFloat() const;
	degreeTypes toDegree() const;
	pairTypes toPair() const;
	stringTypes toString() const;

	float getValue() const {return val;}

	/// implicitly convert to bool
	operator bool() const;
	/// implicitly convert to other types
	operator pairTypes() const;
	operator stringTypes() const;

	/// output stream operator
	friend std::ostream& operator<<(std::ostream& out, const degreeTypes& s);

	/// Negation Operator
	friend degreeTypes operator- (const degreeTypes &s);
	/// Arithmetic Operators
	friend const degreeTypes operator+(const degreeTypes& lhs, const degreeTypes& rhs);
	friend const degreeTypes operator-(const degreeTypes& lhs, const degreeTypes& rhs);
	friend const degreeTypes operator/(const degreeTypes& lhs, const degreeTypes& rhs);
	friend const degreeTypes operator*(const degreeTypes& lhs, const degreeTypes& rhs);
	friend const degreeTypes operator%(const degreeTypes& lhs, const degreeTypes& rhs);
	/// Arithmetic Assignment Operators
	friend degreeTypes& operator+=(degreeTypes& lhs, const degreeTypes& rhs);
	friend degreeTypes& operator-=(degreeTypes& lhs, const degreeTypes& rhs);
	friend degreeTypes& operator*=(degreeTypes& lhs, const degreeTypes& rhs);
	friend degreeTypes& operator/=(degreeTypes& lhs, const degreeTypes& rhs);
	friend degreeTypes& operator%=(degreeTypes& lhs, const degreeTypes& rhs);
	/// Equality Operators
	friend boolTypes operator==(const degreeTypes& left, const degreeTypes& right);
	friend boolTypes operator!=(const degreeTypes& left, const degreeTypes& right);
	/// Logical Operators
	friend boolTypes operator!(const degreeTypes& s);
	friend boolTypes operator&&(const degreeTypes& left, const degreeTypes& right);
	friend boolTypes operator||(const degreeTypes& left, const degreeTypes& right);
protected:
	//TODO: not yet rounded
	float val;
};


///////////////////////////////////////
/// PAIR
///////////////////////////////////////
class pairTypes : public generalTypes {
public:
	/// Constructors
	pairTypes():fx(0.0f), fy(0.0f){};
	pairTypes(float _x, float _y):fx(_x), fy(_y) {}
	explicit pairTypes(const intTypes &_x, const intTypes &_y) { fx = float(_x.getVal()); fy = float(_y.getVal());}
	explicit pairTypes(const floatTypes &_x, const floatTypes &_y) {fx = _x.getVal(); fy = _y.getVal();}

	/// Type conversion functions
	boolTypes toBoolean() const;
	intTypes toInteger() const { assert(0 && "Pair type can not be converted to Int."); return intTypes(-1); };
	floatTypes toFloat() const { assert(0 && "Pair type can not be converted to Float."); return floatTypes(-1.0f); };
	degreeTypes toDegree() const { assert(0 && "Pair type can not be converted to Degree."); return degreeTypes(-1.0f); };
	pairTypes toPair() const;
	stringTypes toString() const;

	float getX() const { return this->fx;}
	float getY() const { return this->fy;}

	/// implicitly convert to bool
	operator bool() const;
	/// implicitly convert to other types
	operator stringTypes() const;

	/// output stream operator
	friend std::ostream& operator<<(std::ostream& out, const pairTypes& s);

	/// Negation Operator
	friend pairTypes operator- (const pairTypes &s);
	/// Arithmetic Operators
	friend const pairTypes operator+(const pairTypes& lhs, const pairTypes& rhs);
	friend const pairTypes operator-(const pairTypes& lhs, const pairTypes& rhs);
	friend const pairTypes operator/(const pairTypes& lhs, const pairTypes& rhs);
	friend const pairTypes operator*(const pairTypes& lhs, const pairTypes& rhs);
	friend const pairTypes operator%(const pairTypes& lhs, const pairTypes& rhs);
	/// Arithmetic Assignment Operators
	friend pairTypes& operator+=(pairTypes& lhs, const pairTypes& rhs);
	friend pairTypes& operator-=(pairTypes& lhs, const pairTypes& rhs);
	friend pairTypes& operator*=(pairTypes& lhs, const pairTypes& rhs);
	friend pairTypes& operator/=(pairTypes& lhs, const pairTypes& rhs);
	friend pairTypes& operator%=(pairTypes& lhs, const pairTypes& rhs);
	/// Equality Operators
	friend boolTypes operator==(const pairTypes& left, const pairTypes& right);
	friend boolTypes operator!=(const pairTypes& left, const pairTypes& right);
	/// Logical Operators
	friend boolTypes operator!(const pairTypes& s);
	friend boolTypes operator&&(const pairTypes& left, const pairTypes& right);
	friend boolTypes operator||(const pairTypes& left, const pairTypes& right);
protected:
	float fx, fy;
};


///////////////////////////////////////
/// STRING
///////////////////////////////////////
class stringTypes : public generalTypes {
public:
	/// Constructors
	stringTypes():str() {};
	stringTypes(const std::string& _str):str(_str) {};

	std::string getStr(){return str;}

	/// Type conversion functions
	boolTypes toBoolean() const;
	intTypes toInteger() const;
	floatTypes toFloat() const;
	degreeTypes toDegree() const;
	pairTypes toPair() const;
	stringTypes toString() const;

	/// implicitly convert to bool
	operator bool() const;

	/// output stream operator
	friend std::ostream& operator<<(std::ostream& out, const stringTypes& s);
	/// String Concatenation Operators
	friend const stringTypes operator+(const stringTypes& lhs, const stringTypes& rhs);
	friend stringTypes& operator+=(stringTypes& lhs, const stringTypes& rhs);
	/// Equality Operators
	friend boolTypes operator==(const stringTypes& left, const stringTypes& right);
	friend boolTypes operator!=(const stringTypes& left, const stringTypes& right);
	/// Logical Operators
	friend boolTypes operator!(const stringTypes& s);
	friend boolTypes operator&&(const stringTypes& left, const stringTypes& right);
	friend boolTypes operator||(const stringTypes& left, const stringTypes& right);
protected:
	std::string str;
};

///////////////////////////////////////
/// IMAGE
///////////////////////////////////////
class imageTypes : public generalTypes {
public:
	/// default constructor
	imageTypes():im(NULL), fx(0.0f), fy(0.0f), pathname(DEFAULT_DIR), filename(DEFAULT_FILENAME), extension(DEFAULT_TYPE) {}
	/// copy constructor
	imageTypes(const imageTypes& img);
	/// load from file name
	imageTypes(const std::string &imageSpec_);
	/// specify size and color
	imageTypes(size_t width_, size_t height_, const Magick::Color &color_);
	/// Destructor
	~imageTypes() {
		//if (im != NULL)
			//delete im;
	}

	Magick::Image* getImage() const {return im;}
	float getX() const {return this->fx;}
	float getY() const {return this->fy;}
	intTypes width(){
		intTypes width((int)im->size().width());
		return width;
	}
	intTypes height(){
		intTypes height((int)im->size().height());
		return height;
	}
	void show() {this->im->write("win:");}
	/*
	void comp(imageTypes dollar){
		this->im->composite(dollar.getImage(),dollar.getX(),dollar.getY());
	}
	*/
	operator bool() const;
	operator boolTypes() const;

	boolTypes toBoolean() const;  
	intTypes toInteger() const { assert(0 && "Image type can not be converted to Int."); return intTypes(-1); }
	floatTypes toFloat() const { assert(0 && "Image type can not be converted to Float."); return floatTypes(-1.0f); }
	degreeTypes toDegree() const { assert(0 && "Image type can not be converted to Degree."); return degreeTypes(-1.0f); }
	pairTypes toPair() const{ assert(0 && "Image type can not be converted to Degree."); return pairTypes(-1.0f,-1.0f); }
	stringTypes toString() const { assert(0 && "Image type can not be converted to Boolean."); return boolTypes(false); }

	friend const imageTypes operator|(const imageTypes& lhs, const pairTypes& rhs); // # tranlstion
	friend const imageTypes operator&(const imageTypes& lhs, const degreeTypes& rhs); // @ rotation
	friend const imageTypes operator/(const imageTypes& lhs, const pairTypes& rhs); // skewing
	friend const imageTypes operator*(const imageTypes& lhs, const pairTypes& rhs); //scaling

	friend imageTypes& operator|=(imageTypes& lhs, const pairTypes& rhs);
	friend imageTypes& operator&=(imageTypes& lhs, const degreeTypes& rhs);
	friend imageTypes& operator*=(imageTypes& lhs, const pairTypes& rhs);
	friend imageTypes& operator/=(imageTypes& lhs, const pairTypes& rhs);

protected:
	Magick::Image *im;
	float fx;
	float fy;
public:
	stringTypes pathname;
	stringTypes filename;
	stringTypes extension;
private:
	static const std::string DEFAULT_DIR;
	static const std::string DEFAULT_FILENAME;
	static const std::string DEFAULT_TYPE;
};

/// Special Handlers for Equality Operators:
///		prevent implicitly conversion from bool to other datatypes
boolTypes operator==(const boolTypes& left, const intTypes& right);
boolTypes operator!=(const boolTypes& left, const intTypes& right);
boolTypes operator==(const intTypes& left, const boolTypes& right);
boolTypes operator!=(const intTypes& left, const boolTypes& right);

boolTypes operator==(const boolTypes& left, const floatTypes& right);
boolTypes operator!=(const boolTypes& left, const floatTypes& right);
boolTypes operator==(const floatTypes& left, const boolTypes& right);
boolTypes operator!=(const floatTypes& left, const boolTypes& right);

boolTypes operator==(const boolTypes& left, const degreeTypes& right);
boolTypes operator!=(const boolTypes& left, const degreeTypes& right);
boolTypes operator==(const degreeTypes& left, const boolTypes& right);
boolTypes operator!=(const degreeTypes& left, const boolTypes& right);

boolTypes operator==(const boolTypes& left, const pairTypes& right);
boolTypes operator!=(const boolTypes& left, const pairTypes& right);
boolTypes operator==(const pairTypes& left, const boolTypes& right);
boolTypes operator!=(const pairTypes& left, const boolTypes& right);

boolTypes operator==(const boolTypes& left, const stringTypes& right);
boolTypes operator!=(const boolTypes& left, const stringTypes& right);
boolTypes operator==(const stringTypes& left, const boolTypes& right);
boolTypes operator!=(const stringTypes& left, const boolTypes& right);

/// Special Handlers for Relational Operators
///		give a warning if implicitly conversion from bool to int or float
boolTypes operator<(const boolTypes& left, const intTypes& right);
boolTypes operator>(const boolTypes& left, const intTypes& right);
boolTypes operator<=(const boolTypes& left, const intTypes& right);
boolTypes operator>=(const boolTypes& left, const intTypes& right);

boolTypes operator<(const intTypes& left, const boolTypes& right);
boolTypes operator>(const intTypes& left, const boolTypes& right);
boolTypes operator<=(const intTypes& left, const boolTypes& right);
boolTypes operator>=(const intTypes& left, const boolTypes& right);

boolTypes operator<(const boolTypes& left, const floatTypes& right);
boolTypes operator>(const boolTypes& left, const floatTypes& right);
boolTypes operator<=(const boolTypes& left, const floatTypes& right);
boolTypes operator>=(const boolTypes& left, const floatTypes& right);

boolTypes operator<(const floatTypes& left, const boolTypes& right);
boolTypes operator>(const floatTypes& left, const boolTypes& right);
boolTypes operator<=(const floatTypes& left, const boolTypes& right);
boolTypes operator>=(const floatTypes& left, const boolTypes& right);

#endif ///__BIPL_TYPES__